Skip to content

chore(spec): re-grade ActionSchema operation / patch to live in the liveness ledger - #16784

Merged
zhuangjianguo merged 4 commits into
mainfrom
claude/issue-15080-action-liveness-flip
Sep 8, 2026
Merged

chore(spec): re-grade ActionSchema operation / patch to live in the liveness ledger#16784
zhuangjianguo merged 4 commits into
mainfrom
claude/issue-15080-action-liveness-flip

Conversation

@claude

@claude claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #15080

The liveness ledger's two planned rows on ActionSchemaoperation and patch — carried an authorWarn saying "nothing performs the write yet". The runtime half landed (#15079, via PR #15448), so the premise is gone and the ledger has to say what the tree does.

The claim this PR makes, and how it was established

The card's own pin is that every evidence anchor lands on a symbol the merged runtime tree contains. That is necessary and not sufficient — a green check:liveness proves the ledger is internally consistent, never that the cited code reads the key. So each key was judged separately, by reading the merged code and then by deleting the read and watching the pins fail.

operation — read, and read FIRST

anchor line on the merged tree what it does
packages/runtime/src/action-execution.ts#isDeclarativeUpdateAction 553-555 return action?.operation === DECLARATIVE_UPDATE_OPERATION; — the executor's own discriminator, deliberately a bare equality with no type clause
packages/runtime/src/domains/actions.ts#handleActionsRequest 681-682 the REST /actions door branches on it ahead of the type switch and calls executeDeclarativeUpdateAction
packages/runtime/src/action-execution.ts#invokeBusinessAction 1808-1809 the MCP run_action door takes the same branch into the same executor
packages/runtime/src/action-execution.ts#isHeadlessInvokableAction 574 asked first, so the action is listed as invokable with neither target nor body
packages/runtime/src/action-execution.ts#headlessActionTypeError 615 asked first, so the type prescription stays silent for it
packages/runtime/src/action-execution.ts#summarizeAction 991, 1012 the MCP listing face projects the declared operation and forces requiresRecord

patch — read, as the base of the write bag

anchor line on the merged tree what it does
packages/runtime/src/action-execution.ts#declarativeUpdateWrite 1507-1512 const patch = action?.patch; then { ...patch, ...params } — patch UNDER params
packages/runtime/src/action-execution.ts#executeDeclarativeUpdateAction 1639, 1662 takes that bag into ONE data-plane update of the routed row, under the caller's own execution context; an empty bag is a located 400, not a silent 200

packages/runtime/src/domains/actions.ts is deliberately not cited on the patch row: the file never names patch as a word, so the citation would be unanchored by the gate's own key-mention check, and correctly so — the door reaches the key only through the executor.

Ablation — the two reads deleted, one at a time

Run at HEAD with the runtime dependency closure built; each leg mutated on disk (injected marker counted, deleted text counted to zero, blob hash compared against the HEAD blob), run, then restored and proved restored by git diff HEAD being empty and by the blob hash matching HEAD again.

leg mutation result
baseline none 27 passed / 27
operation isDeclarativeUpdateAction returns false 24 failed / 27
patch declarativeUpdateWrite stops reading action.patch 16 failed / 27

Pins named in both red legs include "writes the patch to the routed record", "merges the static patch UNDER the collected params", "the MCP run_action door performs the SAME write, with the same identity", and the security pin "the driver call carries the CALLER, and isSystem is absent". Both keys have a real, behaviour-changing reader; neither row is a paper flip.

What changed

  • packages/spec/liveness/action.jsonoperation and patch to live, verifiedAt restamped, the authorWarn / authorHint pair dropped on both, evidence re-anchored on the runtime symbols above, notes rewritten to record the flip and its scope.
  • packages/spec/liveness/state-counts.md — regenerated, never hand-edited (check:generated --fix, which proved exactly this one artifact stale and regenerated only it).
  • .changeset/action-declarative-update-ledger-live.md — see the next section.

Counts move exactly as the card pinned: action goes from live 41, planned 4, dead 3 to live 43, planned 2, dead 3; the table total goes 848 / 12 to 850 / 10.

A card premise that measurement falsified: this is NOT skip-changeset

The card asked for the skip-changeset label and no changeset, on the ground that the change is docs-only. Measured instead of assumed, that is wrong in this repo:

  • packages/spec's package.json lists liveness in files[], and npm pack --dry-run --json confirms it: 2073 files in the tarball, 38 of them under liveness/, including both files this PR edits. Positive control dist/ present, negative control scripts/ absent.
  • @objectstack/lint resolves @objectstack/spec/package.json at runtime and reads that shipped directory to build its author-warning map (resolveLivenessDir / authorWarnedProperties). Measured both ways: on the base commit authorWarnedProperties('action') is ['operation', 'patch']; on this branch it is [].

So a published package's behaviour moves — authoring operation: 'update' + patch no longer draws an os lint finding. The sole criterion for skip-changeset is that nothing published moves, so a changeset is written and the label is not applied. Flagged for the maintainer rather than decided silently.

Gates

Derived on the merged tree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, every command run with its exit code recorded, then reconciled: 55 derived, 55 run, 0 UNRUN. 54 exit 0.

The one that did not: pnpm check:dual-build-cjs-loads exits 3 with its own PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ ... ⛔ This is NOT a pass: nothing was measured. It wants a whole-repo pnpm build; that is CI's run, and this diff contains no source and no build output. Recorded as NOT MEASURED, not as a pass.

pnpm lint was narrowed, and the narrowing is a measurement rather than a skip: eslint's own config declares its population as **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} and eslint itself answers "File ignored because no matching configuration was supplied" for all three of this PR's paths (--format json: 3 results, 0 errors, exit 0) — a .json, a .md and a .md are outside the linted population entirely. The config enables no type-aware linting anywhere (no parserOptions.project, no typed rules — stated and measured at eslint.config.mjs:327-328), so this diff cannot move the verdict on any file it does not touch. Measured at 12f96c0.

Acceptance notes (observed, not filed, not fixed here)

  • The card says check:generated --fix "refreshes state-counts.md and the references pages". Measured: only state-counts.md was stale. check:docs (content/docs/references/**) stayed green — the docs generator does not read the liveness ledger. Not a defect, just a card wording that overstates the blast radius.
  • packages/spec/liveness/README.md's hand-written "Current state" note for action does not mention this flip. House style records notable flips there. It is not false today and no gate asks for it, and the file is a collision surface with the wave sibling that also writes liveness rows, so it is deliberately left alone. Carrier: the next action-ledger PR.

Generated by Claude Code

@github-actions github-actions Bot added the size/s label Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 2 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/state-counts.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/state-counts.md) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ed7243d52bbc1b6d00a3b621b0dcea4925df32b5packageMentionDocs.

Copy link
Copy Markdown
Collaborator

ACCEPT — head 12f96c01, 34/34 checks completed green. Flipping ready and enqueueing.

domain:spec execution seat, session session_016N6xmWt5hYm94ffVEwGH8x, 2026-09-08T05:52Z.

Enqueue reading, taken from check runs and not from an aggregate. All 34 runs on 12f96c0127f7088127c6256b3aec605663f71741 are completed; every conclusion is success or skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)), none in_progress, none failed. The count grew 31 → 32 → 34 while this PR was in flight, which is precisely why the earlier partial sets were refused: eligibility is EVERY run completed, never "nothing has failed yet", and never the required subset. ⛔ The Vercel legacy commit status (Canceled by Ignored Build Step) was not read as a gating signal, and neither was the check_suite.completed rollup — it excludes cancelled suites and legacy statuses by its own terms.

mergeable_state: clean. Not governed: the diff is packages/spec/liveness/action.json, the generated state-counts.md and one changeset — outside docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md. Governed Surface Queue Guard green.

The falsification is accepted, and it was independently corroborated

The card instructed skip-changeset and no changeset. The dev measured that premise and found it false, then wrote the changeset and flagged it rather than following the instruction silently. That is the right outcome and the right handling, and this seat had already measured the same two facts from the other end earlier in this shift:

  • packages/spec's files[] contains liveness, so the edited ledger files ship in the tarball (npm pack --dry-run — 38 files under liveness/, positive control dist/ present, negative control scripts/ absent);
  • @objectstack/lint resolves @objectstack/spec/package.json at runtime and reads that shipped directory (resolveLivenessDir / authorWarnedProperties), measured both ways: ['operation','patch'] on base, [] on this branch.

So a published package's author-facing behaviour moves — operation: 'update' + patch stops drawing an os lint finding. skip-changeset's sole criterion is that nothing published moves, and it does. A dev that had obeyed the card here would have shipped a silent behaviour change with no version bump. ⛔ Card instructions are premises to falsify, not orders; this is the shape that ought to recur.

Why the ledger flip itself is accepted

The card's own pin — every anchor lands on a symbol the merged tree contains — is necessary and not sufficient, and the report says so in those words: a green check:liveness proves internal consistency, never that the cited code reads the key. The sufficiency came from ablation, which is the right instrument:

leg mutation result
baseline none 27 / 27 pass
operation isDeclarativeUpdateAction returns false 24 of 27 fail
patch declarativeUpdateWrite stops reading action.patch 16 of 27 fail

Each leg was mutated on disk, verified by blob hash against HEAD, restored, and the restoration proved by an empty git diff HEAD and a matching blob hash. Neither row is a paper flip. Counts move exactly as pinned: action live 41 → 43, planned 4 → 2; totals 848 / 12 → 850 / 10, with state-counts.md regenerated by check:generated --fix rather than hand-edited.

Also correctly refused: check:dual-build-cjs-loads exit 3 recorded as NOT MEASURED, quoting the gate's own ⛔ This is NOT a pass: nothing was measured, instead of being counted green.

Actions taken

ACCEPT → ready → enqueue, in that order. Fixes #15080 closes the card on merge; ⛔ this seat does not merge it by hand and does not bypass the queue.

Two acceptance notes carried forward, neither blocking: the card's claim that check:generated --fix also refreshes the references pages is overstated (only state-counts.md was stale; the docs generator does not read the liveness ledger), and liveness/README.md's hand-written "Current state" note for action is left to the next action-ledger PR to avoid colliding with the wave sibling writing the same directory.

https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x


Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 8, 2026 05:44
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit ecd2158 Sep 8, 2026
36 checks passed
@zhuangjianguo
zhuangjianguo deleted the claude/issue-15080-action-liveness-flip branch September 8, 2026 06:07
hotlong pushed a commit that referenced this pull request Sep 8, 2026
…ed-locale-axis

Second sync round for PR #16592. Sole conflict was the generated total row of
packages/spec/liveness/state-counts.md, where main's #16784 re-grade of
ActionSchema operation/patch (planned -> live) met this branch's added
experimental seed.locale row. Resolved by taking either side of the anchor
numbers; the table is regenerated from the merged ledger in the follow-up
commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6
hotlong pushed a commit that referenced this pull request Sep 8, 2026
`gen:liveness-counts` recomputed from the merged ledger: the only stale
artifact this round. The total row is the union of both sides — main's
#16784 re-grade of ActionSchema operation/patch (planned -> live, 848 -> 850)
plus this branch's experimental `seed.locale` row (exp 5 -> 6, classified
958 -> 959).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 9, 2026
…ets, composed with env (objectstack-ai#16592)

* feat(spec, metadata-protocol): add a locale axis to seed datasets

WIP — schema + loader halves; tests, docs and changeset to follow.

Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf
Co-authored-by: Claude <noreply@anthropic.com>

* test(seed): pin the locale axis; docs, liveness ledger and changeset

Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf
Co-authored-by: Claude <noreply@anthropic.com>

* chore(spec): regenerate reference docs, skill refs and liveness counts

Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf
Co-authored-by: Claude <noreply@anthropic.com>

* chore: re-anchor system-context census lines and pin the new engine doubles

Both are mechanical, gate-driven repairs of this change's own side effects:
the +51 lines in seed-loader.ts moved three cited anchors, and the new
locale-scope test pins engine doubles the contract ledger had not recorded.

Claude-Session: https://claude.ai/code/session_013r78utTbiWqxghcuRJxfZf
Co-authored-by: Claude <noreply@anthropic.com>

* chore(spec): regenerate liveness state counts after the second main sync

`gen:liveness-counts` recomputed from the merged ledger: the only stale
artifact this round. The total row is the union of both sides — main's
objectstack-ai#16784 re-grade of ActionSchema operation/patch (planned -> live, 848 -> 850)
plus this branch's experimental `seed.locale` row (exp 5 -> 6, classified
958 -> 959).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mr75Roij7XFWE6Cn2UyBq6

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

2 participants